#!/usr/bin/env python
#
# semantic.py is part of pyStrips
# Copyright (C) - 2009 - Juan B. Cabral <jbcabral_89@yahoo.com.ar>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

################################################################################
# DOCS
################################################################################

'''Validate semantic and create structure groups for pyStrips.'''


################################################################################
# IMPORTS
################################################################################

import common
import structs
import error   

 
################################################################################
# PRIVATE FUNCTIONS
################################################################################

def _not_reserved_words(s_group):
    for struct in s_group:
        if struct.name in common.RESERVED_WORDS:
            msg = "'%s' is a reserved word" % struct.name
            raise error.PyStripsError(msg, error.SEMANTIC_ERROR, struct.statement)             
        if isinstance(struct, structs.Predicate) or isinstance(struct, structs.Operator):
            for a in struct.args:
                if a in common.RESERVED_WORDS:
                        msg = "'%s' is a reserved word" % a
                        raise error.PyStripsError(msg,
                                                  error.SEMANTIC_ERROR,
                                                  struct.statement)   
        if isinstance(struct, structs.Operator) or isinstance(struct, structs.State):
            preds = [p for p in struct]
            _not_reserved_words(preds)                   


def _names_not_elements(s_group):
     elements = []
     for p in s_group.predicates:
         elements.extend(p.args)
     for s in s_group:
         if s.name in elements:
             msg = "Name '%s' is an element name" % s.name 
             raise error.PyStripsError(msg, error.SEMANTIC_ERROR, s.statement)


def _elements_in_operators(s_group):
    for op in s_group.operators:
        for pre in op:
            for e in pre.args:
                if e not in op.args:
                    msg = "Element '%s' not in operator argument" % e
                    raise error.PyStripsError(msg,
                                              error.SEMANTIC_ERROR,
                                              pre.statement)


def _predicates_exists(s_group):
    error_info = ()
    for struct in s_group:
        if isinstance(struct, structs.Operator) or isinstance(struct, structs.State):
            for p in struct:
                if p not in s_group.predicates:
                        msg = "Predicate '%s' not declared" % p.statement
                        raise error.PyStripsError(msg, error.SEMANTIC_ERROR,
                                                  p.statement)    
    

def _elements_not_repeated(s_group):
    for struct in s_group:           
        if isinstance(struct, structs.Predicate) or isinstance(struct, structs.Operator):
            for a in struct.args:
                if struct.args.count(a) > 1:
                    msg = "Element '%s' repeated" % a
                    raise error.PyStripsError(msg, error.SEMANTIC_ERROR, struct.statement)   
        if isinstance(struct, structs.Operator) or isinstance(struct, structs.State):
            preds = [p for p in struct]
            _elements_not_repeated(preds)  
    

def _predicates_not_repeated(s_group):
    for struct in s_group:           
        if isinstance(struct, structs.Predicate):
            for p in s_group:
                if isinstance(p, structs.Predicate) \
                and p is not struct and p == struct:
                    p.args.sort()
                    struct.args.sort()
                    if p.args == struct.args:
                        msg = "Predicate '%s' repeated" % struct.name
                        raise error.PyStripsError(msg, error.SEMANTIC_ERROR, struct.statement)
        elif isinstance(struct, structs.State):
            preds = [p for p in struct]
            _predicates_not_repeated(preds)  
        elif isinstance(struct, structs.Operator):
            preds = [p for p in struct.pre_list]
            _predicates_not_repeated(preds)
            preds = [p for p in struct.del_list]
            _predicates_not_repeated(preds)  
            preds = [p for p in struct.add_list]
            _predicates_not_repeated(preds)
            

def _names_not_repeated(s_group):
    names = {}
    for struct in s_group:
        key = str(type(struct))
        if  key not in names:
            names[key] = []
        if struct.name in names[key]:
            msg = "Duplicated name '%s'" % struct.name
            raise error.PyStripsError(msg, error.SEMANTIC_ERROR, struct.statement)
        else:
            names[key].append(struct.name)


################################################################################
# PUBLIC FUNCTIONS
################################################################################
    
def validate(struct_list):
    s_group = structs.StructureGroup(struct_list)
    _not_reserved_words(s_group)
    _names_not_repeated(s_group)
    _predicates_not_repeated(s_group)
    _predicates_exists(s_group)
    _elements_not_repeated(s_group)
    _elements_in_operators(s_group)
    _names_not_elements(s_group)
    return s_group


################################################################################
# MAIN
################################################################################

if __name__ == '__main__':
    print(__doc__)
        
        
